home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2010 April / PCA177.iso / ESSENTIALS / Firefox Setup.exe / nonlocalized / components / nsAddonRepository.js < prev    next >
Encoding:
Text File  |  2009-07-15  |  11.4 KB  |  350 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 38 "e:\builds\moz2_slave\win32_build\build\toolkit\mozapps\extensions\src\nsAddonRepository.js"
  4. */
  5.  
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. const Cr = Components.results;
  9.  
  10. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  11.  
  12. const PREF_GETADDONS_BROWSEADDONS        = "extensions.getAddons.browseAddons";
  13. const PREF_GETADDONS_BROWSERECOMMENDED   = "extensions.getAddons.recommended.browseURL";
  14. const PREF_GETADDONS_GETRECOMMENDED      = "extensions.getAddons.recommended.url";
  15. const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
  16. const PREF_GETADDONS_GETSEARCHRESULTS    = "extensions.getAddons.search.url";
  17.  
  18. const XMLURI_PARSE_ERROR  = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
  19.  
  20. const API_VERSION = "1.2";
  21.  
  22. function AddonSearchResult() {
  23. }
  24.  
  25. AddonSearchResult.prototype = {
  26.   id: null,
  27.   name: null,
  28.   version: null,
  29.   summary: null,
  30.   description: null,
  31.   rating: null,
  32.   iconURL: null,
  33.   thumbnailURL: null,
  34.   homepageURL: null,
  35.   eula: null,
  36.   type: null,
  37.   xpiURL: null,
  38.   xpiHash: null,
  39.  
  40.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonSearchResult])
  41. }
  42.  
  43. function AddonRepository() {
  44. }
  45.  
  46. AddonRepository.prototype = {
  47.   // The current set of results
  48.   _addons: null,
  49.  
  50.   // Whether we are currently searching or not
  51.   _searching: false,
  52.  
  53.   // Is this a search for recommended add-ons
  54.   _recommended: false,
  55.  
  56.   // XHR associated with the current request
  57.   _request: null,
  58.  
  59.   // Callback object to notify on completion
  60.   _callback: null,
  61.  
  62.   // Maximum number of results to return
  63.   _maxResults: null,
  64.  
  65.   get homepageURL() {
  66.     return Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  67.                      .getService(Components.interfaces.nsIURLFormatter)
  68.                      .formatURLPref(PREF_GETADDONS_BROWSEADDONS);
  69.   },
  70.  
  71.   get isSearching() {
  72.     return this._searching;
  73.   },
  74.  
  75.   getRecommendedURL: function() {
  76.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  77.                          .getService(Components.interfaces.nsIURLFormatter);
  78.  
  79.     return urlf.formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED);
  80.   },
  81.  
  82.   getSearchURL: function(aSearchTerms) {
  83.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  84.                           .getService(Components.interfaces.nsIPrefBranch);
  85.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  86.                          .getService(Components.interfaces.nsIURLFormatter);
  87.  
  88.     var url = prefs.getCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS);
  89.     url = url.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
  90.     return urlf.formatURL(url);
  91.   },
  92.  
  93.   cancelSearch: function() {
  94.     this._searching = false;
  95.     if (this._request) {
  96.       this._request.abort();
  97.       this._request = null;
  98.     }
  99.     this._callback = null;
  100.     this._addons = null;
  101.   },
  102.  
  103.   retrieveRecommendedAddons: function(aMaxResults, aCallback) {
  104.     if (this._searching)
  105.       return;
  106.  
  107.     this._searching = true;
  108.     this._addons = [];
  109.     this._callback = aCallback;
  110.     this._recommended = true;
  111.     this._maxResults = aMaxResults;
  112.  
  113.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  114.                           .getService(Components.interfaces.nsIPrefBranch);
  115.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  116.                          .getService(Components.interfaces.nsIURLFormatter);
  117.  
  118.     var uri = prefs.getCharPref(PREF_GETADDONS_GETRECOMMENDED);
  119.     uri = uri.replace(/%API_VERSION%/g, API_VERSION);
  120.     uri = urlf.formatURL(uri);
  121.     this._loadList(uri);
  122.   },
  123.  
  124.   searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
  125.     if (this._searching)
  126.       return;
  127.  
  128.     this._searching = true;
  129.     this._addons = [];
  130.     this._callback = aCallback;
  131.     this._recommended = false;
  132.     this._maxResults = aMaxResults;
  133.  
  134.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  135.                           .getService(Components.interfaces.nsIPrefBranch);
  136.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  137.                          .getService(Components.interfaces.nsIURLFormatter);
  138.  
  139.     var uri = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS);
  140.     uri = uri.replace(/%API_VERSION%/g, API_VERSION);
  141.     // We double encode due to bug 427155
  142.     uri = uri.replace(/%TERMS%/g, encodeURIComponent(encodeURIComponent(aSearchTerms)));
  143.     uri = urlf.formatURL(uri);
  144.     this._loadList(uri);
  145.   },
  146.  
  147.   // Posts results to the callback
  148.   _reportSuccess: function(aCount) {
  149.     this._searching = false;
  150.     this._request = null;
  151.     // The callback may want to trigger a new search so clear references early
  152.     var addons = this._addons;
  153.     var callback = this._callback;
  154.     this._callback = null;
  155.     this._addons = null;
  156.     callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aCount);
  157.   },
  158.  
  159.   // Notifies the callback of a failure
  160.   _reportFailure: function(aEvent) {
  161.     this._searching = false;
  162.     this._request = null;
  163.     // The callback may want to trigger a new search so clear references early
  164.     var callback = this._callback;
  165.     this._callback = null;
  166.     this._addons = null;
  167.     callback.searchFailed();
  168.   },
  169.  
  170.   // Parses an add-on entry from an <addon> element
  171.   _parseAddon: function(element) {
  172.     var em = Cc["@mozilla.org/extensions/manager;1"].
  173.              getService(Ci.nsIExtensionManager);
  174.     var app = Cc["@mozilla.org/xre/app-info;1"].
  175.               getService(Ci.nsIXULAppInfo).
  176.               QueryInterface(Ci.nsIXULRuntime);
  177.  
  178.     var guid = element.getElementsByTagName("guid");
  179.     if (guid.length != 1)
  180.       return;
  181.  
  182.     // Ignore add-ons already seen in the results
  183.     for (var i = 0; i < this._addons.length; i++)
  184.       if (this._addons[i].id == guid[0].textContent)
  185.         return;
  186.  
  187.     // Ignore installed add-ons
  188.     if (em.getItemForID(guid[0].textContent) != null)
  189.       return;
  190.  
  191.     // Ignore sandboxed add-ons
  192.     var status = element.getElementsByTagName("status");
  193.     // The status element has a unique id for each status type. 4 is Public.
  194.     if (status.length != 1 || status[0].getAttribute("id") != 4)
  195.       return;
  196.  
  197.     // Ignore add-ons not compatible with this OS
  198.     var os = element.getElementsByTagName("compatible_os");
  199.     // Only the version 0 schema included compatible_os if it isn't there then
  200.     // we will see os compatibility on the install elements.
  201.     if (os.length > 0) {
  202.       var compatible = false;
  203.       var i = 0;
  204.       while (i < os.length && !compatible) {
  205.         if (os[i].textContent == "ALL" || os[i].textContent == app.OS) {
  206.           compatible = true;
  207.           break;
  208.         }
  209.         i++;
  210.       }
  211.       if (!compatible)
  212.         return;
  213.     }
  214.  
  215.     // Ignore add-ons not compatible with this Application
  216.     compatible = false;
  217.     var tags = element.getElementsByTagName("compatible_applications");
  218.     if (tags.length != 1)
  219.       return;
  220.     var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
  221.              getService(Ci.nsIVersionComparator);
  222.     var apps = tags[0].getElementsByTagName("appID");
  223.     var i = 0;
  224.     while (i < apps.length) {
  225.       if (apps[i].textContent == app.ID) {
  226.         var minversion = apps[i].parentNode.getElementsByTagName("min_version")[0].textContent;
  227.         var maxversion = apps[i].parentNode.getElementsByTagName("max_version")[0].textContent;
  228.         if ((vc.compare(minversion, app.version) > 0) ||
  229.             (vc.compare(app.version, maxversion) > 0))
  230.           return;
  231.         compatible = true;
  232.         break;
  233.       }
  234.       i++;
  235.     }
  236.     if (!compatible)
  237.       return;
  238.  
  239.     var addon = new AddonSearchResult();
  240.     addon.id = guid[0].textContent;
  241.     addon.rating = -1;
  242.     var node = element.firstChild;
  243.     while (node) {
  244.       if (node instanceof Ci.nsIDOMElement) {
  245.         switch (node.localName) {
  246.           case "name":
  247.           case "version":
  248.           case "summary":
  249.           case "description":
  250.           case "eula":
  251.             addon[node.localName] = node.textContent;
  252.             break;
  253.           case "rating":
  254.             if (node.textContent.length > 0) {
  255.               var rating = parseInt(node.textContent);
  256.               if (rating >= 0)
  257.                 addon.rating = Math.min(5, rating);
  258.             }
  259.             break;
  260.           case "thumbnail":
  261.             addon.thumbnailURL = node.textContent;
  262.             break;
  263.           case "icon":
  264.             addon.iconURL = node.textContent;
  265.             break;
  266.           case "learnmore":
  267.             addon.homepageURL = node.textContent;
  268.             break;
  269.           case "type":
  270.             // The type element has an id attribute that is the id from AMO's
  271.             // database. This doesn't match our type values to perform a mapping
  272.             if (node.getAttribute("id") == 2)
  273.               addon.type = Ci.nsIUpdateItem.TYPE_THEME;
  274.             else
  275.               addon.type = Ci.nsIUpdateItem.TYPE_EXTENSION;
  276.             break;
  277.           case "install":
  278.             // No os attribute means the xpi is compatible with any os
  279.             if (node.hasAttribute("os")) {
  280.               var os = node.getAttribute("os").toLowerCase();
  281.               // If the os is not ALL and not the current OS then ignore this xpi
  282.               if (os != "all" && os != app.OS.toLowerCase())
  283.                 break;
  284.             }
  285.             addon.xpiURL = node.textContent;
  286.             if (node.hasAttribute("hash"))
  287.               addon.xpiHash = node.getAttribute("hash");
  288.             break;
  289.         }
  290.       }
  291.       node = node.nextSibling;
  292.     }
  293.  
  294.     // Add only if there was an xpi compatible with this os
  295.     if (addon.xpiURL)
  296.       this._addons.push(addon);
  297.   },
  298.  
  299.   // Called when a single request has completed, parses out any add-ons and
  300.   // either notifies the callback or does a new request for more results
  301.   _listLoaded: function(aEvent) {
  302.     var request = aEvent.target;
  303.     var responseXML = request.responseXML;
  304.  
  305.     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
  306.         (request.status != 200 && request.status != 0)) {
  307.       this._reportFailure();
  308.       return;
  309.     }
  310.     var elements = responseXML.documentElement.getElementsByTagName("addon");
  311.     for (var i = 0; i < elements.length; i++) {
  312.       this._parseAddon(elements[i]);
  313.  
  314.       var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  315.                             .getService(Components.interfaces.nsIPrefBranch);
  316.       if (this._addons.length == this._maxResults) {
  317.         this._reportSuccess(elements.length);
  318.         return;
  319.       }
  320.     }
  321.  
  322.     if (responseXML.documentElement.hasAttribute("total_results"))
  323.       this._reportSuccess(responseXML.documentElement.getAttribute("total_results"));
  324.     else
  325.       this._reportSuccess(elements.length);
  326.   },
  327.  
  328.   // Performs a new request for results
  329.   _loadList: function(aURI) {
  330.     this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  331.                     createInstance(Ci.nsIXMLHttpRequest);
  332.     this._request.open("GET", aURI, true);
  333.     this._request.overrideMimeType("text/xml");
  334.  
  335.     var self = this;
  336.     this._request.onerror = function(event) { self._reportFailure(event); };
  337.     this._request.onload = function(event) { self._listLoaded(event); };
  338.     this._request.send(null);
  339.   },
  340.  
  341.   classDescription: "Addon Repository",
  342.   contractID: "@mozilla.org/extensions/addon-repository;1",
  343.   classID: Components.ID("{8eaaf524-7d6d-4f7d-ae8b-9277b324008d}"),
  344.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonRepository])
  345. }
  346.  
  347. function NSGetModule(aCompMgr, aFileSpec) {
  348.   return XPCOMUtils.generateModule([AddonRepository]);
  349. }
  350.